// ============================================================================
//  TANGRAM CHINO — las 7 piezas para OpenSCAD
//  Lado del cuadrado completo: 140 mm   ·   Grosor: 5 mm
//
//  Mismas proporciones y misma disposición que la aplicación «Tangram Chino».
//
//  Los vértices se expresan en CUARTOS DE LADO (0..4), que es la unidad en la
//  que TODOS caen en números enteros: el tangram completo es el cuadrado
//  [0,4] x [0,4]. La constante U = LADO/4 los convierte a milímetros.
//
//  Uso rápido:
//    F5 = vista previa · F6 = render · Archivo > Exportar > Exportar como STL
//    DISPOSICION = "explosionado" → forma del tangram con las piezas algo
//                                   separadas (por defecto)
//    DISPOSICION = "montado"      → las 7 piezas formando el cuadrado exacto
//    DISPOSICION = "fila"         → piezas apartadas y ordenadas para imprimir
//    SOLO_PIEZA  = 0..6           → una sola pieza (para un STL por pieza)
// ============================================================================


/* [Medidas] */

// Lado del cuadrado del tangram completo (mm)
LADO = 140;

// Grosor de las piezas (mm)
GROSOR = 5;

// Holgura por cara (mm). Se resta al contorno de cada pieza.
//   0           → medidas matemáticas exactas: encajan a presión y cuesta
//                 separarlas, pero el conjunto mide exactamente 140 mm.
//   0.15 – 0.25 → impresión 3D FDM (compensa la expansión del filamento).
//   0.05 – 0.10 → corte láser (la mitad del kerf de la máquina).
HOLGURA = 0;


/* [Disposición] */

DISPOSICION = "junta"; // [junta, explosionado, montado, fila]

// Anchura de la junta entre piezas en la disposición "junta" (mm). Las piezas
// conservan su tamaño exacto: solo se apartan, cada una en su dirección, de
// modo que el hueco mide JUNTA en TODAS las juntas.
JUNTA = 3;

// Cuánto se despegan las piezas en "explosionado". Es la fracción que se
// alejan del centro del cuadrado: 0.12 = un 12 % más lejos. Ojo, ahí los
// huecos NO son iguales: van de 3,5 a 9,9 mm con LADO = 140.
EXPLOSION = 0.12;

// Separación entre piezas en "fila", en cuartos de lado (0.35 = 12,25 mm)
SEPARACION = 0.35;

// -1 = todas las piezas · 0..6 = solo la indicada (para exportar STL sueltos)
SOLO_PIEZA = -1;

// Colores de la aplicación en la vista previa (no afecta al STL)
COLOREAR = true;


/* [Marca CITED] */

// Graba «CITED» en la cara trasera (la de arriba al imprimir) de UNA pieza.
//   true  → con marca (por defecto)
//   false → sin marca
MARCA_CITED = true;

// Profundidad del grabado (mm). 0.6 es la misma muesca que las regletas.
MARCA_PROFUNDIDAD = 0.6;

// Pieza que lleva la marca (nombre de la lista PIEZAS), posición del centro del
// texto respecto al centro de la caja de la pieza (mm) y giro (grados).
MARCA_PIEZA = "cuadrado";
MARCA_DESPL = [0.00, 0.00];
MARCA_GIRO  = 0;


// ─── Geometría ──────────────────────────────────────────────────────────────

U = LADO / 4;        // un cuarto de lado, en mm
g = SEPARACION;
CENTRO = [2, 2];     // centro del cuadrado, en cuartos de lado

//  [ nombre, polígono, desplazamiento en "fila", color, desplazamiento en
//    "junta" por cada milímetro de hueco ]
//
//  El polígono es la posición REAL de la pieza dentro del cuadrado montado, en
//  cuartos de lado. Ni "fila" ni "junta" giran ni deforman la pieza: solo la
//  apartan, así que las medidas son siempre las de un tangram de LADO mm.
//
//  El último campo sale de resolver el sistema «cada una de las 10 juntas del
//  tangram se abre exactamente lo mismo». Tiene solución exacta y es la que
//  está escrita aquí; está recentrada para que la figura no se desplace.
PIEZAS = [
    ["triangulo_grande_rojo",   [[0,4],[0,0],[2,2]],       [0,      0    ], "#D32F2F", [-0.707106,  0.505076]],
    ["triangulo_grande_azul",   [[4,4],[0,4],[2,2]],       [2+g,    g    ], "#1565C0", [ 0.000000,  1.212183]],
    ["triangulo_mediano",       [[4,2],[2,0],[4,0]],       [g,      0    ], "#FF9800", [ 0.707107, -0.909137]],
    ["cuadrado",                [[2,2],[3,3],[4,2],[3,1]], [2+2*g, -1    ], "#9C27B0", [ 0.426777,  0.224746]],
    ["triangulo_pequeno_cian",  [[1,1],[3,1],[2,2]],       [5+3*g, -1    ], "#00BCD4", [-0.280330, -0.482361]],
    ["triangulo_pequeno_verde", [[4,2],[4,4],[3,3]],       [3+3*g, -1+g  ], "#4CAF50", [ 1.133882,  0.931853]],
    ["paralelogramo",           [[1,1],[3,1],[2,0],[0,0]], [2+g,    4+2*g], "#FFEB3B", [-1.280330, -1.482361]]
];


// Media de los vértices. En estas siete piezas (triángulos, un cuadrado y un
// paralelogramo) coincide con el centro de gravedad de la figura.
function suma(v, i = 0) = i < len(v) ? v[i] + suma(v, i + 1) : 0;
function centroide(pts) = [suma([for (p = pts) p[0]]) / len(pts),
                           suma([for (p = pts) p[1]]) / len(pts)];

// En "explosionado" cada pieza se aleja del centro del cuadrado en proporción
// a lo lejos que ya estaba. Al ser todas las piezas convexas, esta homotecia
// garantiza que dos piezas vecinas siempre se separan y nunca se cruzan.
function desplazamiento(datos) =
    DISPOSICION == "junta"        ? datos[4] * (JUNTA / U) :
    DISPOSICION == "explosionado" ? (centroide(datos[1]) - CENTRO) * EXPLOSION :
    DISPOSICION == "fila"         ? datos[2]
                                  : [0, 0];   // "montado"


// ─── Marca CITED ────────────────────────────────────────────────────────────
// «CITED» en letras de bloque (5 mm de alto, trazo de 1 mm, unos 21 mm de
// ancho), centrado en [0, 0]. Se graba en la cara trasera de una sola pieza.
module marca_texto() {
    // C
    polygon([[-5.992, 1.673], [-6.318, 1.976], [-6.693, 2.216], [-7.105, 2.386], [-7.541, 2.481], [-7.986, 2.496], [-8.427, 2.433], [-8.849, 2.292], [-9.240, 2.078], [-9.587, 1.798], [-9.878, 1.462], [-10.105, 1.078], [-10.261, 0.661], [-10.340, 0.223], [-10.340, -0.223], [-10.261, -0.661], [-10.105, -1.078], [-9.878, -1.462], [-9.587, -1.798], [-9.240, -2.078], [-8.849, -2.292], [-8.427, -2.433], [-7.986, -2.496], [-7.541, -2.481], [-7.105, -2.386], [-6.693, -2.216], [-6.318, -1.976], [-5.992, -1.673], [-6.735, -1.004], [-6.931, -1.186], [-7.156, -1.330], [-7.403, -1.432], [-7.664, -1.488], [-7.931, -1.498], [-8.196, -1.460], [-8.449, -1.375], [-8.684, -1.247], [-8.892, -1.079], [-9.067, -0.877], [-9.203, -0.647], [-9.297, -0.397], [-9.344, -0.134], [-9.344, 0.134], [-9.297, 0.397], [-9.203, 0.647], [-9.067, 0.877], [-8.892, 1.079], [-8.684, 1.247], [-8.449, 1.375], [-8.196, 1.460], [-7.931, 1.498], [-7.664, 1.488], [-7.403, 1.432], [-7.156, 1.330], [-6.931, 1.186], [-6.735, 1.004]]);
    // I
    polygon([[-4.450, -2.500], [-3.450, -2.500], [-3.450, 2.500], [-4.450, 2.500]]);
    // T
    polygon([[-2.450, 2.500], [1.150, 2.500], [1.150, 1.500], [-0.150, 1.500], [-0.150, -2.500], [-1.150, -2.500], [-1.150, 1.500], [-2.450, 1.500]]);
    // E
    polygon([[2.150, -2.500], [5.750, -2.500], [5.750, -1.500], [3.150, -1.500], [3.150, -0.500], [5.350, -0.500], [5.350, 0.500], [3.150, 0.500], [3.150, 1.500], [5.750, 1.500], [5.750, 2.500], [2.150, 2.500]]);
    // D
    difference() {
        polygon([[6.750, -2.500], [7.850, -2.500], [7.850, -2.500], [8.261, -2.466], [8.662, -2.365], [9.040, -2.199], [9.386, -1.973], [9.689, -1.693], [9.943, -1.367], [10.139, -1.004], [10.274, -0.614], [10.341, -0.206], [10.341, 0.206], [10.274, 0.614], [10.139, 1.004], [9.943, 1.367], [9.689, 1.693], [9.386, 1.973], [9.040, 2.199], [8.662, 2.365], [8.261, 2.466], [7.850, 2.500], [6.750, 2.500]]);
        polygon([[7.750, -1.500], [7.850, -1.500], [7.850, -1.500], [8.097, -1.480], [8.337, -1.419], [8.564, -1.319], [8.771, -1.184], [8.954, -1.016], [9.106, -0.820], [9.224, -0.603], [9.304, -0.368], [9.345, -0.124], [9.345, 0.124], [9.304, 0.368], [9.224, 0.603], [9.106, 0.820], [8.954, 1.016], [8.771, 1.184], [8.564, 1.319], [8.337, 1.419], [8.097, 1.480], [7.850, 1.500], [7.750, 1.500]]);
    }
}

// Graba la marca en la pieza `datos` si es la elegida y MARCA_CITED es true.
// Debe llamarse dentro de un difference() y ya con la pieza colocada en su sitio.
module marca_cited(datos) {
    if (MARCA_CITED && datos[0] == MARCA_PIEZA) {
        xs = [for (p = datos[1]) p[0] * U];
        ys = [for (p = datos[1]) p[1] * U];
        c = [(min(xs) + max(xs)) / 2, (min(ys) + max(ys)) / 2];
        translate([c[0] + MARCA_DESPL[0], c[1] + MARCA_DESPL[1], GROSOR - MARCA_PROFUNDIDAD])
            rotate(MARCA_GIRO)
                linear_extrude(height = MARCA_PROFUNDIDAD + 1)
                    marca_texto();
    }
}


module pieza(datos) {
    d = desplazamiento(datos);
    translate([d[0] * U, d[1] * U, 0])
        difference() {
            linear_extrude(height = GROSOR)
                offset(delta = -HOLGURA)
                    polygon([for (p = datos[1]) [p[0] * U, p[1] * U]]);
            marca_cited(datos);
        }
}

module tangram() {
    for (i = [0 : len(PIEZAS) - 1]) {
        if (SOLO_PIEZA < 0 || SOLO_PIEZA == i) {
            if (COLOREAR) { color(PIEZAS[i][3]) pieza(PIEZAS[i]); }
            else          { pieza(PIEZAS[i]); }
        }
    }
}

tangram();


// ─── Comprobación de medidas (se ve en la consola al pulsar F5) ─────────────

echo(str("Lado del cuadrado completo: ", LADO, " mm"));
echo(str("Cateto del triangulo grande: ", 2 * U * sqrt(2), " mm"));
echo(str("Lado del cuadrado pequeno:   ", U * sqrt(2), " mm"));
echo(str("Grosor: ", GROSOR, " mm   ·   Holgura por cara: ", HOLGURA, " mm"));


// ============================================================================
//  NOTAS
//
//  · LAS PIEZAS MIDEN SIEMPRE LO QUE DEBEN, en las cuatro disposiciones:
//    ninguna las encoge, las gira ni las deforma. Con LADO = 140 (14 cm) la
//    hipotenusa del triángulo grande mide 140,000 mm, su cateto 98,995 mm y
//    el lado del cuadrado 49,497 mm.
//
//  · Dos maneras de ver la figura completa con las piezas despegadas:
//
//    "junta"        Cada pieza se aparta en su propia dirección, la justa
//                   para que las 10 juntas del tangram se abran exactamente
//                   lo mismo. Hueco constante = JUNTA en todas. El conjunto
//                   ocupa 147 x 148 mm con JUNTA = 3.
//
//    "explosionado" Cada pieza se aleja del centro en proporción a lo lejos
//                   que ya estaba. Más sencillo de entender, pero los huecos
//                   NO son iguales: con EXPLOSION = 0.12 van de 3,5 mm
//                   (cian-amarillo) a 9,9 mm (naranja-verde).
//
//  · Todas las piezas se generan apoyadas en el plano XY, que es como deben
//    imprimirse o cortarse: sin soportes y con la cara vista abajo.
//
//  · El paralelogramo es la única pieza QUIRAL (no coincide consigo misma al
//    darle la vuelta). En un tangram físico eso da igual porque la pieza se
//    puede voltear; si la imprimes con dos colores por cara, ten en cuenta
//    que la figura reflejada necesita la pieza del revés.
//
//  · Con HOLGURA = 0 las siete piezas suman exactamente el cuadrado de
//    140 mm, pero al imprimirlas no se podrán separar bien. Para un juego
//    manipulable pon 0.2 y el conjunto medirá unas cuatro décimas menos.
//
//  · Para exportar un STL por pieza: pon SOLO_PIEZA = 0, exporta, luego 1,
//    exporta, y así hasta 6. El orden es el de la lista PIEZAS.
//
//  · Para corte láser conviene exportar en 2D en vez de STL: comenta la
//    llamada a linear_extrude (o pon GROSOR muy pequeño) y usa
//    Exportar como DXF / SVG con DISPOSICION = "fila".
// ============================================================================
